package name.jautzy.esi.server.model;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

import name.jautzy.cedb.model.Bonus;
import name.jautzy.cedb.model.Category;
import name.jautzy.cedb.model.Equipment;
import name.jautzy.cedb.model.Group;
import name.jautzy.esi.client.model.Fit;
import name.jautzy.esi.server.EdbDAO;
import name.jautzy.esi.server.util.DigestUtils;

public class FitFactory {
	private Fit fit;
	private FitProxy fitProxy;
	private EdbDAO dao;
	
	private long bonusSkillLevel = 5;
	private double launcherCpuNeedBonus= 1.0;
	private double turretCpuNeedBonus= 1.0;
	private double launcherPowerNeedBonus= 1.0;
	private double remEnergyPowerNeedBonus= 1.0;
	private double turretPowerNeedBonus = 1.0;
	private double aftSpeedBonus=1.0;
	
	private Equipment ship= null;
	
	private List<Equipment> rigs;
	private List<Equipment> modules;
	private List<Equipment> subsystems;

	private Equipment launcher= null;
	private long launcherNb = 0;

	private Equipment turret= null;
	private long turretNb = 0;

	private Equipment drone = null;
	private long droneNb = 0;
	
	private double eCMCapNeedBonus= 1.0;
	private double turretCapNeedBonus= 1.0;
	private double sensorUpgradeCpuNeedBonus= 1.0;
	private double scrambleWebCapNeedBonus= 1.0;
	private double sensorCapNeedBonus= 1.0;
	private double painterCapNeedBonus= 1.0;
	private double disruptCapNeedBonus= 1.0;
	private double passiveShieldHardenerBonus= 1.0;
	private double remEnergyCapNeedBonus= 1.0;
	private double gridUpgradeCpuNeedBonus= 1.0;
	private double shieldBoosterCapNeedBonus= 1.0;
	private double armorRepairCapNeedBonus= 1.0;
	private double remShieldCapNeedBonus= 1.0;
	private double shieldUpgradePowerNeedBonus= 1.0;
	private double rigDrawbackReduction= 1.0;
	private double passiveArmorHardenerBonus= 1.0;
	private double remArmorCapNeedBonus= 1.0;
	private double remHullCapNeedBonus= 1.0;
	private double afterburnerDurationBonus= 1.0;
	private double mwdCapNeedBonus= 1.0;
	private double mwdSigBonus= 1.0;
	private double remArmorPowerNeedBonus= 1.0;
	private double remShieldCpuNeedBonus= 1.0;
	private double scrambleCapNeedBonus = 1.0;
	
	private HashMap<String,ArrayList<Double>> moduleCorrections;
	private HashMap<String,ArrayList<Double>> moduleCorrectionsNoStack;
	
	public FitFactory(String usermail) {
		fit = new Fit();
		fitProxy = new FitProxy();
		fitProxy.setMaker(usermail);
		fit.setMaker(usermail);
		rigs = new LinkedList<Equipment>();
		modules = new LinkedList<Equipment>();
		subsystems = new LinkedList<Equipment> ();
		applyModuleUsageSkills();
		applySetSkills();
		moduleCorrections = new HashMap<String,ArrayList<Double>>();
		moduleCorrectionsNoStack = new HashMap<String,ArrayList<Double>>();
	}
	
	public Fit getFit() {
		return fit;
	}
	
	public FitProxy getFitProxy() {
		return fitProxy;
	}
	
	public void parseString(String input, EdbDAO dao) throws IOException {
		fit.setEftFit(input);
		this.dao = dao;
		String line;
		try {
			BufferedReader reader = new BufferedReader(new StringReader(input));
			while((line=reader.readLine())!=null) {
				line = line.trim();
				if (!line.contains("empty")) {
					if (line.contains("[")) {
						String newline = line.substring(1,line.length()-1);
						String[] tokens = newline.split(",");
						if (tokens.length!=2)
							throw new IOException("Bad EFT file : can't find Ship Name");
						fitProxy.setName(tokens[1].trim());
						fitProxy.setShipName(tokens[0].trim());
						setShip();
					}
					else if (line.contains(",")) {
						String[] tokens = line.split(",");
						if (tokens.length!=2)
							throw new IOException("Bad EFT file : can't find Ammo");
						String moduleName = tokens[0].trim();
						String ammoName = tokens[1].trim();
						addModule(moduleName,ammoName,1);
						
					}
					else if (!line.isEmpty()) {
						String newline = line.replaceAll(" x[1-9]","");
						long nb = 1;
						if (!newline.equals(line))
							nb = Long.parseLong(line.substring(line.length()-1, line.length()));
						addModule(newline,null,nb);
					}
				}
			}
			
			String fitId =generateId();
			fitProxy.setFitId(fitId);
			fit.setId(fitId);
			
		}
		catch (IOException e) {
			throw e;
		}
	}
	
	public void constructAll() throws IOException {
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		constructShip();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		constructSubsystems();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		constructRigs();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		applyBonuses();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		if (turret!=null)
			constructTurret();
		if (launcher!=null)
			constructLauncher();
		if (drone!=null)
			constructDrone();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		constructModules();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		applyModuleCorrections();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		applyCorrectSkills();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		fieldCalculations();
		//System.out.println(fit.getMass()+fit.getBoostedMass());
		
	}
	
	private void setShip() {
		Equipment equip = dao.getEquipmentFor(fitProxy.getShipName());
		ship = equip;
	}
	
	private void addModule(String moduleName, String ammoName, long nb) throws IOException {
		Equipment equip = dao.getEquipmentFor(moduleName);
		if (equip==null)
			return;
		Equipment ammo = dao.getEquipmentFor(ammoName);
		
		HashSet<String> effects = dao.getEffectsNameAsSetFor(equip);
		
		if (effects.contains("hiPower")) {
			fit.getHighSlots().add(equip.getName());
			fit.getHighSlotsIcon().add(equip.getIcon());
		}
		else if (effects.contains("loPower")) {
			fit.getLowSlots().add(equip.getName());
			fit.getLowSlotsIcon().add(equip.getIcon());
		}
		else if (effects.contains("medPower")) {
			fit.getMedSlots().add(equip.getName());
			fit.getMedSlotsIcon().add(equip.getIcon());
		}
		else if (effects.contains("rigSlot")) {
			fit.getRigSlots().add(equip.getName());
			fit.getRigSlotsIcon().add(equip.getIcon());
		}
		else if (effects.contains("subSystem")) {
			fit.getSubsystems().add(equip.getName());
			fit.getSubsystemsIcon().add(equip.getIcon());
		}
		
		Group group = dao.getGroupFor(equip);
		Category cat = dao.getCategoryFor(group);
		String catName = cat.getCatName();
		String groupName = group.getGroupName();
		
		if (catName.equals("Subsystem"))
			subsystems.add(equip);
		else if (groupName.contains("Rig"))
			rigs.add(equip);
		else if (catName.equals("Drone")) 
			addDrone(equip,nb);
		else if (groupName.contains("Launcher") && !groupName.contains("Probe"))
			addLauncher(equip,ammo);
		else if (groupName.contains("Weapon"))
			addWeapon(equip,ammo);
		else 
			addModule(equip,ammo);
		
	}
	
	private void addLauncher(Equipment equip, Equipment ammo) throws IOException {
		if (launcher==null) {
			launcher = equip;
			launcherNb = 1;
			launcher.setAmmo(ammo);
		}
		else if (launcher.getName().equals(equip.getName()))
			launcherNb+=1;
		else
			throw new IOException("Bad EFT file : multiple launcher types");
	}
	
	private void addWeapon(Equipment equip, Equipment ammo) throws IOException {
		if (turret==null) {
			turret = equip;
			turretNb = 1;
			turret.setAmmo(ammo);
		}
		else if (turret.getName().equals(equip.getName()))
			turretNb+=1;
		else
			throw new IOException("Bad EFT file : multiple turret types");
	}
	
	private void addModule(Equipment equip, Equipment ammo) {
		equip.setAmmo(ammo);
		modules.add(equip);
	}

	private void addDrone(Equipment equip,long nb) throws IOException {
		if (drone==null) {
			drone = equip;
			droneNb = nb;
		}
		else if (drone.getName().equals(equip.getName()))
			droneNb+=nb;
		else
			throw new IOException("Bad EFT file : multiple drone types");
	}
	
	private void constructSubsystems() {
		for (Equipment equip : subsystems)
			applySubsystem(equip);
	}

	private void applySubsystem(Equipment equip) {
		HashMap<String,Double> attributes = dao.getAttributesAsMapFor(equip);
		
		fit.addMass(equip.getMass());
		fit.addHighSlots(convertLong(attributes.get("hiSlotModifier")));
		fit.addMedSlots(convertLong(attributes.get("medSlotModifier")));
		fit.addLowSlots(convertLong(attributes.get("lowSlotModifier")));
		fit.addTurrets(convertLong(attributes.get("turretHardPointModifier")));
		fit.addLaunchers(convertLong(attributes.get("launcherHardPointModifier")));	
		fit.addMaxCpu(convertDouble(attributes.get("cpuOutput")));
		fit.addMaxPower(convertDouble(attributes.get("powerOutput")));
		fit.addCapacitor(convertDouble(attributes.get("capacitorCapacity")));
		double caprech = convertDouble(attributes.get("rechargeRate"))/1000.0;
		if (caprech!=0)
			fit.setCapRechargeTime(caprech);
		
		fit.addShieldHP(convertDouble(attributes.get("shieldCapacity")));
		double srt = convertDouble(attributes.get("shieldRechargeRate"));
		if (srt!=0)
			fit.setShieldRechTime(srt/1000.0);
		double emSR = convertDouble(attributes.get("passiveShieldEmDamageResonance"));
		if (emSR!=0)
			fit.setEmSR(emSR);
		double thSR = convertDouble(attributes.get("passiveShieldThermalDamageResonance"));
		if (thSR!=0)
			fit.setThSR(thSR);
		double exSR = convertDouble(attributes.get("passiveShieldExplosiveDamageResonance"));
		if (exSR!=0)
			fit.setExSR(exSR);
		double kiSR = convertDouble(attributes.get("passiveShieldKineticDamageResonance"));
		if (kiSR!=0)
			fit.setKiSR(kiSR);
		
		double sigRadius = convertDouble(attributes.get("signatureRadius"));
		if (sigRadius!=0)
			fit.setSignature(sigRadius);
		double targetRange = convertDouble(attributes.get("maxTargetRange"));
		if (targetRange!=0)
			fit.setTargetRange(targetRange);
		double sstrength = convertDouble(attributes.get("scanGravimetricStrength"));
		sstrength+=convertDouble(attributes.get("scanLadarStrength"));
		sstrength+=convertDouble(attributes.get("scanMagnetometricStrength"));
		sstrength+=convertDouble(attributes.get("scanRadarStrength"));
		if (sstrength!=0)
			fit.setSensorStrength(sstrength);
		double scanReso = convertDouble(attributes.get("scanResolution"));
		if (scanReso!=0)
			fit.setScanResolution(scanReso);
		
		double velo = convertDouble(attributes.get("maxVelocity"));
		if (velo!=0)
			fit.setVelocity(velo+fit.getVelocity());
		double agility = convertDouble(attributes.get("agility"));
		if (agility!=0)
			fit.setAgility(agility);
		long warpImmune = convertLong(attributes.get("warpBubbleImmuneModifier"));
		if (warpImmune != 0)
			fit.setWarpImmune(true);
		
		fit.addArmorHP(convertDouble(attributes.get("armorHPBonusAdd")));
		double emAR = convertDouble(attributes.get("passiveArmorEmDamageResonance"));
		if (emAR!=0)
			fit.setEmAR(emAR);
		double thAR = convertDouble(attributes.get("passiveArmorThermalDamageResonance"));
		if (thAR!=0)
			fit.setThAR(thAR);
		double exAR = convertDouble(attributes.get("passiveArmorExplosiveDamageResonance"));
		if (exAR!=0)
			fit.setExAR(exAR);
		double kiAR = convertDouble(attributes.get("passiveArmorKineticDamageResonance"));
		if (kiAR!=0)
			fit.setKiAR(kiAR);
		
		fit.addDroneCapacity(convertDouble(attributes.get("droneCapacity")));
		fit.addDroneBw(convertDouble(attributes.get("droneBandwidth")));
	}
	
	private void constructShip() throws IOException {
		
		Group group = dao.getGroupFor(ship);
		Category cat = dao.getCategoryFor(group);
		if (!cat.getCatName().equals("Ship"))
			throw new IOException("Bad EFT file : can't find ship");
		
		String groupName = group.getGroupName();
		if (groupName.equals("Titan")||groupName.equals("Dreadnought")||
			groupName.equals("Carrier")||groupName.equals("Supercarrier")||
			groupName.equals("Capital Industrial Ship")||groupName.equals("Jump Freighter")||
			groupName.equals("Industrial Command Ship"))
			fit.setCapship(true);
		fitProxy.setShipName(ship.getName());
		fit.setShipId(ship.getEqId());
		fit.setMass(ship.getMass());
		fitProxy.setRace(ship.getRace());
		fit.setCargohold(ship.getCapacity());
		fitProxy.setGroupName(group.getGroupName());
		
		if (drone!= null) {
			fit.setDroneName(drone.getName());
			fit.setNbDrones(droneNb);
		}
		if (turret!=null) {
			fit.setTurretName(turret.getName());
			fit.setNbTurrets(turretNb);
			if (turret.getAmmo()!=null)
				fit.setTurretAmmoName(turret.getAmmo().getName());
			else
				throw new IOException("Bad EFT fit : must include ammo");
		}
		if (launcher!=null) {
			fit.setLauncherName(launcher.getName());
			fit.setNbLaunchers(launcherNb);
			if (launcher.getAmmo()!=null)
				fit.setLauncherAmmoName(launcher.getAmmo().getName());
			else
				throw new IOException("Bad EFT fit : must include ammo");
		}
			
		HashMap<String,Double> attributes = dao.getAttributesAsMapFor(ship);
		
		fitProxy.setTechLevel(convertLong(attributes.get("techLevel")));
		
		fit.setMaxHighSlots(convertLong(attributes.get("hiSlots")));
		fit.setMaxMedSlots(convertLong(attributes.get("medSlots")));
		fit.setMaxLowSlots(convertLong(attributes.get("lowSlots")));
		fit.setMaxRigSlots(convertLong(attributes.get("rigSlots")));
		fit.setMaxTurrets(convertLong(attributes.get("turretSlotsLeft")));
		fit.setMaxLaunchers(convertLong(attributes.get("launcherSlotsLeft")));
		fit.setMaxCalibration(convertLong(attributes.get("upgradeCapacity")));
		fit.setMaxCpu(convertDouble(attributes.get("cpuOutput")));
		fit.setCapacitor(convertDouble(attributes.get("capacitorCapacity")));
		fit.setMaxPower(convertDouble(attributes.get("powerOutput")));
		fit.setCapRechargeTime(convertDouble(attributes.get("rechargeRate"))/1000.0);
		double sstrength = convertDouble(attributes.get("scanGravimetricStrength"));
		sstrength+=convertDouble(attributes.get("scanLadarStrength"));
		sstrength+=convertDouble(attributes.get("scanMagnetometricStrength"));
		sstrength+=convertDouble(attributes.get("scanRadarStrength"));
		fit.setSensorStrength(sstrength);
		fit.setScanResolution(convertDouble(attributes.get("scanResolution")));
		
				
		fit.setAgility(convertDouble(attributes.get("agility")));
		fit.setWarpSpeed(convertDouble(attributes.get("baseWarpSpeed")));
		fit.setVelocity(convertDouble(attributes.get("maxVelocity")));
		fit.setSignature(convertDouble(attributes.get("signatureRadius")));
		
		fit.setEmSR(convertDouble(attributes.get("shieldEmDamageResonance")));
		fit.setExSR(convertDouble(attributes.get("shieldExplosiveDamageResonance")));
		fit.setKiSR(convertDouble(attributes.get("shieldKineticDamageResonance")));
		fit.setThSR(convertDouble(attributes.get("shieldThermalDamageResonance")));
		fit.setShieldHP(convertDouble(attributes.get("shieldCapacity")));
		fit.setShieldRechTime(convertDouble(attributes.get("shieldRechargeRate"))/1000.0);
		
		fit.setEmAR(convertDouble(attributes.get("armorEmDamageResonance")));
		fit.setExAR(convertDouble(attributes.get("armorExplosiveDamageResonance")));
		fit.setKiAR(convertDouble(attributes.get("armorKineticDamageResonance")));
		fit.setThAR(convertDouble(attributes.get("armorThermalDamageResonance")));
		fit.setArmorHP(convertDouble(attributes.get("armorHP")));
		
		fit.setStructResonance(convertDouble(attributes.get("emDamageResonance")));
		fit.setStructHP(convertDouble(attributes.get("hp")));
		
		fit.setDroneCapacity(convertDouble(attributes.get("droneCapacity")));
		fit.setMaxDroneBw(convertDouble(attributes.get("droneBandwidth")));
		
		fit.setMaxTarget(convertLong(attributes.get("maxLockedTargets")));
		fit.setTargetRange(convertDouble(attributes.get("maxTargetRange")));
		

		
		
	}
	
	private void applyBonus(Equipment equip) {
		Class<FitBonusApply> c = FitBonusApply.class;
		FitBonusApply bonusApply = new FitBonusApply(fit,this);
	
		for (Bonus b : dao.getBonus(equip.getName())) {
			try {
				Method m = c.getDeclaredMethod("apply"+b.getBonusName(),Bonus.class);
				m.invoke(bonusApply,b);
			}
			catch (Exception exc) {
				System.out.println(exc.toString());
			}
		}	
	}
	
	private void constructRigs() {
		for (Equipment equip : rigs) {
			applyModule(equip);
		}
	}
	
	private void constructLauncher () {
		HashMap<String,Double> attributes = getDao().getAttributesAsMapFor(launcher);
		HashMap<String,Double> ammoAttributes = getDao().getAttributesAsMapFor(launcher.getAmmo());
		
		fit.setMissileRof(convertDouble(attributes.get("speed"))/1000.0);
		fit.setMissileDRF(convertDouble(ammoAttributes.get("aoeDamageReductionFactor")));
		fit.setMissileExploRadius(convertDouble(ammoAttributes.get("aoeCloudSize")));
		fit.setMissileExploVelocity(convertDouble(ammoAttributes.get("aoeVelocity")));
		fit.setMissileFlightTime(convertDouble(ammoAttributes.get("explosionDelay"))/1000.0);
		fit.setMissileVelocity(convertDouble(ammoAttributes.get("maxVelocity")));
		double alpha = convertDouble(ammoAttributes.get("emDamage"));
		alpha+=convertDouble(ammoAttributes.get("kineticDamage"));
		alpha+=convertDouble(ammoAttributes.get("explosiveDamage"));
		alpha+=convertDouble(ammoAttributes.get("thermalDamage"));
		fit.setMissileAlpha(launcherNb*alpha);
		long tech = convertLong(attributes.get("techLevel"));
		if (tech==2)
			fit.setMissileTech2(true);
		
		
		double sigBonus = convertDouble(ammoAttributes.get("signatureRadiusBonus"))/100.0;
		
		if (sigBonus!=0) {
			double sigCorrection = Math.pow(1.0+sigBonus,launcherNb);
			addCorrectionNoStack("Signature",sigCorrection-1.0);
		}
		
		double basevc = convertDouble(ammoAttributes.get("maxVelocityBonus"));
		
		if (basevc!=0) {
			double veloCorrection = Math.pow(basevc,launcherNb);
			addCorrectionNoStack("Velocity",veloCorrection-1.0);
		}
		
		fit.addCpu(launcherNb*convertDouble(attributes.get("cpu"))*launcherCpuNeedBonus);
		fit.addPower(launcherNb*convertDouble(attributes.get("power"))*launcherPowerNeedBonus);
	}
	
	private void constructTurret() {
		HashMap<String,Double> attributes = getDao().getAttributesAsMapFor(turret);
		HashMap<String,Double> ammoAttributes = getDao().getAttributesAsMapFor(turret.getAmmo());
		
		double optiMultiplier = convertDouble(ammoAttributes.get("weaponRangeMultiplier"));
		if (optiMultiplier==0)
			optiMultiplier = 1;
		double falloffMultiplier = convertDouble(ammoAttributes.get("fallofMultiplier"));
		if (falloffMultiplier==0)
			falloffMultiplier=1;
		double trackingMultiplier = convertDouble(ammoAttributes.get("trackingSpeedMultiplier"));
		if (trackingMultiplier==0)
			trackingMultiplier=1;
		double rofMultiplier = convertDouble(ammoAttributes.get("speedMultiplier"));
		if (rofMultiplier==0)
			rofMultiplier=1;
		
		fit.setTurretTracking(convertDouble(attributes.get("trackingSpeed"))*trackingMultiplier);
		fit.setTurretSignature(convertDouble(attributes.get("optimalSigRadius")));
		fit.setTurretFalloff(convertDouble(attributes.get("falloff"))*falloffMultiplier);
		fit.setTurretOptimal(convertDouble(attributes.get("maxRange"))*optiMultiplier);
		fit.setTurretRof(convertDouble(attributes.get("speed"))/1000.0*rofMultiplier);
		double alpha = convertDouble(ammoAttributes.get("emDamage"));
		alpha+=convertDouble(ammoAttributes.get("explosiveDamage"));
		alpha+=convertDouble(ammoAttributes.get("kineticDamage"));
		alpha+=convertDouble(ammoAttributes.get("thermalDamage"));
		alpha*=convertDouble(attributes.get("damageMultiplier"));
		fit.setTurretAlpha(turretNb*alpha);
		long tech = convertLong(attributes.get("techLevel"));
		if (tech==2)
			fit.setTurretTech2(true);
		
		double caprechmalus = convertDouble(ammoAttributes.get("capacitorRechargeRateMultiplier"));
		if (caprechmalus != 0) {
			caprechmalus = Math.pow(caprechmalus,turretNb);
			addCorrectionNoStack("CapRechargeRate",caprechmalus-1.0);
		}
		
		double velomalus = convertDouble(ammoAttributes.get("maxVelocityBonus"));
		if (velomalus != 0) {
			velomalus = Math.pow(velomalus,turretNb);
			addCorrectionNoStack("Velocity",velomalus-1.0);
		}
		
		double shieldmalus = convertDouble(ammoAttributes.get("shieldCapacityMultiplier"));
		if (shieldmalus != 0) {
			shieldmalus = Math.pow(shieldmalus,turretNb);
			addCorrectionNoStack("ShieldHP",shieldmalus-1.0);
		}
		
		double sigmalus = convertDouble(ammoAttributes.get("signatureRadiusMultiplier"));
		if (sigmalus != 0) {
			sigmalus = Math.pow(sigmalus,turretNb);
			addCorrectionNoStack("Signature",sigmalus-1.0);
		}
		
		double capmalus = 1.0+convertDouble(ammoAttributes.get("capNeedBonus"))/100.0;
		capmalus = Math.pow(capmalus, turretNb);
		
		fit.addCpu(turretNb*convertDouble(attributes.get("cpu"))*turretCpuNeedBonus);
		fit.addPower(turretNb*convertDouble(attributes.get("power"))*turretPowerNeedBonus);
		double capUsage = attributes.get("capacitorNeed");
		fit.setTurretCapa(turretNb*capUsage*turretCapNeedBonus*capmalus);
	}
	
	private void constructDrone() {
		HashMap<String,Double> attributes = getDao().getAttributesAsMapFor(drone);
		
		fit.addDroneBw(convertDouble(attributes.get("droneBandwidthUsed"))*droneNb);
		double alpha = convertDouble(attributes.get("emDamage"));
		alpha+=convertDouble(attributes.get("explosiveDamage"));
		alpha+=convertDouble(attributes.get("kineticDamage"));
		alpha+=convertDouble(attributes.get("thermalDamage"));
		alpha*=convertDouble(attributes.get("damageMultiplier"));
		fit.setDroneAlpha(alpha*droneNb);
		fit.setDroneTracking(convertDouble(attributes.get("trackingSpeed")));
		fit.setDroneVelocity(convertDouble(attributes.get("maxVelocity")));
		fit.setDroneSignature(convertDouble(attributes.get("optimalSigRadius")));
		fit.setDroneFalloff(convertDouble(attributes.get("falloff")));
		fit.setDroneOptimal(convertDouble(attributes.get("maxRange")));
		fit.setDroneRof(convertDouble(attributes.get("speed"))/1000.0);
		long tech = convertLong(attributes.get("techLevel"));
		if (tech==2)
			fit.setDroneTech2(true);
		
	}
	
	private void constructModules() {
		for (Equipment equip : modules) {
			applyModule(equip);
		}

	}
	
	private void applyModule(Equipment equip) {		
		Class<FitModuleApply> c = FitModuleApply.class;
		Group group = dao.getGroupFor(equip);
		FitModuleApply moduleApply = new FitModuleApply(equip, group, this);

			try {
				Method m = c.getDeclaredMethod("apply"+removeSpace(group.getGroupName()));
				m.invoke(moduleApply);
			}
			catch (Exception exc) {
				System.out.println(exc.toString());
			}
	}
	
	private void applyModuleCorrections() {
		sortCorrections();
		for (Entry<String,ArrayList<Double>> e: moduleCorrections.entrySet()) {
			ArrayList<Double> corrections = e.getValue();
			double result = 1;
			for (int i=0; i<corrections.size();i++) {
				result*=(1+corrections.get(i).doubleValue()*FitModuleApply.stackCoef[i]);
			}
			try {
				Class<Fit> c = Fit.class;
				Method m = c.getDeclaredMethod("correct"+e.getKey(),double.class);
				m.invoke(fit,result);
			}
			catch (Exception exc) {
				System.out.println(exc.toString());
			}
		}
		
		for (Entry<String,ArrayList<Double>> e: moduleCorrectionsNoStack.entrySet()) {
			ArrayList<Double> corrections = e.getValue();
			double result = 1;
			for (int i=0; i<corrections.size();i++) {
				result*=(1+corrections.get(i).doubleValue());
			}
			try {
				Class<Fit> c = Fit.class;
				Method m = c.getDeclaredMethod("correct"+e.getKey(),double.class);
				m.invoke(fit,result);
			}
			catch (Exception exc) {
				System.out.println(exc.toString());
			}
		}
	}
	
	private void applyCorrectSkills() {
		fit.correctMaxCpu(1.0+0.05*bonusSkillLevel); // Electronics.Electronics
		fit.correctTargetRange(1.0+0.05*bonusSkillLevel); //Electronics.LongRangeTargetting
		fit.correctScanResolution(1.0+0.05*bonusSkillLevel); //Electonics.SignatureAnalysis
		if (fit.isDroneTech2())
			fit.correctDroneAlpha(1.0+0.02*bonusSkillLevel); //Drones.[Race]DroneSpecialization
		fit.correctDroneAlpha(1.0+0.05*bonusSkillLevel); //Drones.[Type]DroneOperation
		fit.correctDroneAlpha(1.0+0.2*bonusSkillLevel); //Drones.DroneInterfacing
		fit.correctDroneVelocity(1.0+0.05*bonusSkillLevel); //Drones.DroneNavigation
		fit.correctDroneOptimal(1.0+0.05*bonusSkillLevel); //Drones.DroneSharpshooting
		fit.correctCapacitor(1.0+0.05*bonusSkillLevel); //Engineering.EnergyManagement
		fit.correctCapRechargeTime(1.0-0.05*bonusSkillLevel); //Engineering.EnergySystemsOperation
		fit.correctMaxPower(1.0+0.05*bonusSkillLevel); //Engineering.Engineering
		fit.correctShieldHP(1.0+0.05*bonusSkillLevel); //Engineering.ShieldManagement
		fit.correctShieldRechTime(1.0-0.05*bonusSkillLevel); //Engineering.ShieldOperation
		fit.correctTurretRof(1.0-0.02*bonusSkillLevel); //Gunnery.Gunnery
		if (fit.isTurretTech2())
			fit.correctTurretAlpha(1.0+0.02*bonusSkillLevel); //Gunnery.[Type]Specialization)
		fit.correctTurretAlpha(1.0+0.05*bonusSkillLevel); //Gunnery.[Type]Turret
		fit.correctTurretTracking(1.0+0.05*bonusSkillLevel); //Gunnery.MotionPrediction
		fit.correctTurretRof(1.0-0.04*bonusSkillLevel); //Gunnery.RapidFiring
		fit.correctTurretOptimal(1.0+0.05*bonusSkillLevel); //Gunnery.Sharpshooter
		fit.correctTurretAlpha(1.0+0.03*bonusSkillLevel); //Gunnery.SurgicalStrike
		fit.correctTurretFalloff(1.0+0.05*bonusSkillLevel); //Gunnery.TrajectoryAnalysis
		fit.correctArmorHP(1.0+0.05*bonusSkillLevel); //Mechanic.HullUpgrade
		fit.correctStructHP(1.0+0.05*bonusSkillLevel); //Mechanic.Mechanic
		fit.correctArmorRepairDuration(1.0-0.05*bonusSkillLevel); //Mechanic.RepairSystems
		if (fit.isMissileTech2())
			fit.correctMissileRof(1.0-0.02*bonusSkillLevel); //MissileLauncherOperation.[Type]MissilesSpecialization
		fit.correctMissileAlpha(1.0+0.05*bonusSkillLevel); //MissileLauncherOperation.[Type]Missiles
		if (fit.isTorpedo()==false)
			fit.correctMissileExploRadius(1.0-0.05*bonusSkillLevel); //MissileLauncherOperation.GuidedMissilePrecision
		fit.correctMissileFlightTime(1.0+0.1*bonusSkillLevel); //MissileLauncherOperation.MissileBombardment
		fit.correctMissileRof(1.0-0.02*bonusSkillLevel); //MissileLauncherOperation.MissileLauncherOperation
		fit.correctMissileVelocity(1.0+0.1*bonusSkillLevel); //MissileLauncherOperation.MissileProjection
		fit.correctMissileRof(1.0-0.03*bonusSkillLevel); //MissileLauncherOperation.RapidLaunch
		fit.correctMissileExploVelocity(1.0+0.1*bonusSkillLevel); //MissileLauncherOperation.TargetNavigationPrediction
		fit.correctMissileAlpha(1.0+0.02*bonusSkillLevel); //MissileLauncherOperation.WarheadUpgrades	
		if (fit.hasBooster())
			fit.correctBoostVeloFactor(1.0+0.05*bonusSkillLevel); //Navigation.AccelerationControl
		fit.correctAgility(1.0-0.05*bonusSkillLevel); //Navigation.EvasiveManeuvering
		fit.correctVelocity(1.0+0.05*bonusSkillLevel); //Navigation.Navigation
		fit.correctAgility(1.0-0.02*bonusSkillLevel); //Navigation.SpaceshipCommand
		if (fit.getCapship())
			fit.correctAgility(1.0-0.05*bonusSkillLevel); //Navigation.CapitalShips		
		if (fit.getBoostRadiusFactor()==1.0) //afterburner
			fit.correctBoostCapUsage(1.0-0.1*bonusSkillLevel);//Navigation.FuelConservation
		else // mwd
			fit.correctBoostCapUsage(1.0-0.05*bonusSkillLevel); //Navigation.HighSpeedManeuvering
	}
	
	private void applySetSkills() {
		fit.setMaxDrones(1*bonusSkillLevel); // Drones.Drones
		fit.setDroneRange(20000+5000*bonusSkillLevel); // Scout Drone Operation
	}
	
	private void applyModuleUsageSkills() {
		bonusSkillLevel = 5;
		launcherCpuNeedBonus = 1.0-0.05*bonusSkillLevel; // Gunnery.WeaponUpgrade
		turretCpuNeedBonus = 1.0-0.05*bonusSkillLevel; // Gunnery.WeaponUpgrade
		launcherPowerNeedBonus = 1.0-0.02*bonusSkillLevel; // Gunnery.AdvancedWeaponUpgrade
		turretPowerNeedBonus = 1.0-0.02*bonusSkillLevel; // Gunnery.AdvancedWeaponUpgrade
		setTurretCapNeedBonus(1.0-0.05*bonusSkillLevel); // Gunnery.ControlledBursts
		seteCMCapNeedBonus(1.0-0.05*bonusSkillLevel); //Electronics.ElectronicWarfare
		setSensorUpgradeCpuNeedBonus(1.0-0.05*bonusSkillLevel); //Electronics.ElectronicsUpgrade
		setScrambleWebCapNeedBonus(1.0-0.05*bonusSkillLevel); //Electronics.PropulsionJamming
		setSensorCapNeedBonus(1.0-0.05*bonusSkillLevel); //Electronics.SensorLinking
		setPainterCapNeedBonus(1.0-0.05*bonusSkillLevel); //Electronics.TargetPainting
		setDisruptCapNeedBonus(1.0-0.05*bonusSkillLevel); //Electronics.WeaponDisruption
		setPassiveShieldHardenerBonus(1.0+0.05*bonusSkillLevel); //Engineering.[Type]ShieldCompensation
		setRemEnergyCapNeedBonus(1.0-0.05*bonusSkillLevel); //Engineering.EnergyEmissionSystems
		setGridUpgradeCpuNeedBonus(1.0-0.05*bonusSkillLevel); //Engineering.EnergyGridUpgrade
		setShieldBoosterCapNeedBonus(1.0-0.02*bonusSkillLevel); //Engineering.ShieldCompensation
		setRemShieldCapNeedBonus(1.0-0.05*bonusSkillLevel); //Engineering.ShieldEmissionSystems
		setShieldUpgradePowerNeedBonus(1.0-0.05*bonusSkillLevel); //Engineering.ShieldUpgrades
		rigDrawbackReduction = 1.0-0.1*bonusSkillLevel; //Mechanic.[Type]Rigging
		setPassiveArmorHardenerBonus(1.0+0.05*bonusSkillLevel); //Mechanic.[Type]ArmorCompensation
		setRemArmorCapNeedBonus(1.0-0.05*bonusSkillLevel); //Mechanic.RemoteArmorRepairSystems
		setRemHullCapNeedBonus(1.0-0.05*bonusSkillLevel); //Mechanic.RemoteArmorRepairSystems	
		setAfterburnerDurationBonus(1.0+0.1*bonusSkillLevel); //Mechanic.RemoteArmorRepairSystems	
		
	}
	
	private void applyBonuses() {
		applyBonus(ship);
		for (Equipment sub : subsystems) 
			applyBonus(sub);
	}
	
	void fieldCalculations() {
		//DPS
		double droneRof = fit.getDroneRof();
		double droneDps = 0;
		if (droneRof !=0)
			droneDps = fit.getDroneAlpha()/droneRof;
		
		fit.setMaxDroneDps(droneDps);
		
		double turretRof = fit.getTurretRof();
		double turretDps = 0;
		if (turretRof !=0)
			turretDps = fit.getTurretAlpha()/turretRof;
		fit.setMaxTurretDps(turretDps);
		
		double missileRof = fit.getMissileRof();
		double missileDps = 0;
		if (missileRof !=0)
			missileDps = fit.getMissileAlpha()/missileRof;
		fit.setMaxMissileDps(missileDps);
		
		fitProxy.setMaxDps(droneDps+turretDps+missileDps);
		
		//EFF HP
		double shieldR = (fit.getEmSR()+fit.getExSR()+fit.getThSR()+fit.getKiSR())/4.0;
		double shieldEffHp = fit.getShieldHP()/shieldR; 
		
		double armorR = (fit.getEmAR()+fit.getExAR()+fit.getThAR()+fit.getKiAR())/4.0;
		double armorEffHp = fit.getArmorHP()/armorR;
		
		double structEffHp = fit.getStructHP()/fit.getStructResonance();
		
		fitProxy.setEffectiveHp(shieldEffHp+armorEffHp+structEffHp);
		
		//Boosted Speed
		if (fit.hasBooster()) {
			fit.setBoostedVelocity(fit.getVelocity()*(1+fit.getBoostVeloFactor()*fit.getBoostMass()/(fit.getMass()+fit.getBoostedMass())));
		}
		else
			fit.setBoostedVelocity(fit.getVelocity());
	}
	
	// Public Accessors
	public EdbDAO getDao() {
		return dao;
	}
	
	public void correctLauncherPowerNeedBonus(double b) {
		this.launcherPowerNeedBonus *= b;
	}

	public void setLauncherCpuNeedBonus(double launcherCpuNeedBonus) {
		this.launcherCpuNeedBonus = launcherCpuNeedBonus;
	}

	public double getLauncherCpuNeedBonus() {
		return launcherCpuNeedBonus;
	}
	
	public void setTurretCpuNeedBonus(double turretCpuNeedBonus) {
		this.turretCpuNeedBonus = turretCpuNeedBonus;
	}

	public double getTurretCpuNeedBonus() {
		return turretCpuNeedBonus;
	}
	
	public double getTurretPowerNeedBonus() {
		return turretPowerNeedBonus;
	}
	
	public void setTurretPowerNeedBonus(double turretPowerNeedBonus) {
		this.turretPowerNeedBonus = turretPowerNeedBonus;
	}
	
	public double getRigDrawbackReduction() {
		return rigDrawbackReduction;
	}
	
	public void seteCMCapNeedBonus(double eCMCapNeedBonus) {
		this.eCMCapNeedBonus = eCMCapNeedBonus;
	}

	public double geteCMCapNeedBonus() {
		return eCMCapNeedBonus;
	}
	
	public void correcteCMCapNeedBonus(double b) {
		this.eCMCapNeedBonus *= b;
	}
	
	
	public void setTurretCapNeedBonus(double turretCapNeedBonus) {
		this.turretCapNeedBonus = turretCapNeedBonus;
	}
	
	public void correctTurretCapNeedBonus(double b) {
		this.turretCapNeedBonus *= b;
	}

	public double getTurretCapNeedBonus() {
		return turretCapNeedBonus;
	}

	public void setSensorUpgradeCpuNeedBonus(double sensorUpgradeCpuNeedBonus) {
		this.sensorUpgradeCpuNeedBonus = sensorUpgradeCpuNeedBonus;
	}

	public double getSensorUpgradeCpuNeedBonus() {
		return sensorUpgradeCpuNeedBonus;
	}

	public void setScrambleWebCapNeedBonus(double scrambleWebCapNeedBonus) {
		this.scrambleWebCapNeedBonus = scrambleWebCapNeedBonus;
	}
	
	public void correctScrambleWebCapNeedBonus(double b) {
		this.scrambleWebCapNeedBonus *= b;
	}

	public double getScrambleWebCapNeedBonus() {
		return scrambleWebCapNeedBonus;
	}

	public void setSensorCapNeedBonus(double sensorCapNeedBonus) {
		this.sensorCapNeedBonus = sensorCapNeedBonus;
	}
	
	public void correctSensorCapNeedBonus(double b) {
		this.sensorCapNeedBonus *= b;
	}

	public double getSensorCapNeedBonus() {
		return sensorCapNeedBonus;
	}

	public void setPainterCapNeedBonus(double painterCapNeedBonus) {
		this.painterCapNeedBonus = painterCapNeedBonus;
	}

	public double getPainterCapNeedBonus() {
		return painterCapNeedBonus;
	}

	public void setDisruptCapNeedBonus(double disruptCapNeedBonus) {
		this.disruptCapNeedBonus = disruptCapNeedBonus;
	}

	public double getDisruptCapNeedBonus() {
		return disruptCapNeedBonus;
	}

	public void setPassiveShieldHardenerBonus(double passiveShieldHardenerBonus) {
		this.passiveShieldHardenerBonus = passiveShieldHardenerBonus;
	}

	public double getPassiveShieldHardenerBonus() {
		return passiveShieldHardenerBonus;
	}

	public void setRemEnergyCapNeedBonus(double remEnergyCapNeedBonus) {
		this.remEnergyCapNeedBonus = remEnergyCapNeedBonus;
	}
	
	public void correctRemEnergyCapNeedBonus(double b) {
		this.remEnergyCapNeedBonus *= b;
	}

	public double getRemEnergyCapNeedBonus() {
		return remEnergyCapNeedBonus;
	}

	public void setGridUpgradeCpuNeedBonus(double gridUpgradeCpuNeedBonus) {
		this.gridUpgradeCpuNeedBonus = gridUpgradeCpuNeedBonus;
	}

	public double getGridUpgradeCpuNeedBonus() {
		return gridUpgradeCpuNeedBonus;
	}

	public void setShieldBoosterCapNeedBonus(double shieldBoosterCapNeedBonus) {
		this.shieldBoosterCapNeedBonus = shieldBoosterCapNeedBonus;
	}

	public double getShieldBoosterCapNeedBonus() {
		return shieldBoosterCapNeedBonus;
	}

	public void setRemShieldCapNeedBonus(double remShieldCapNeedBonus) {
		this.remShieldCapNeedBonus = remShieldCapNeedBonus;
	}
	
	public void correctRemShieldCapNeedBonus(double b) {
		this.remShieldCapNeedBonus *= b;
	}

	public double getRemShieldCapNeedBonus() {
		return remShieldCapNeedBonus;
	}

	public void setShieldUpgradePowerNeedBonus(double shieldUpgradePowerNeedBonus) {
		this.shieldUpgradePowerNeedBonus = shieldUpgradePowerNeedBonus;
	}

	public double getShieldUpgradePowerNeedBonus() {
		return shieldUpgradePowerNeedBonus;
	}

	public void setPassiveArmorHardenerBonus(double passiveArmorHardenerBonus) {
		this.passiveArmorHardenerBonus = passiveArmorHardenerBonus;
	}

	public double getPassiveArmorHardenerBonus() {
		return passiveArmorHardenerBonus;
	}

	public void setRemArmorCapNeedBonus(double remArmorCapNeedBonus) {
		this.remArmorCapNeedBonus = remArmorCapNeedBonus;
	}
	
	public void correctRemArmorCapNeedBonus(double b) {
		this.remArmorCapNeedBonus *= b;
	}

	public double getRemArmorCapNeedBonus() {
		return remArmorCapNeedBonus;
	}

	public void setRemHullCapNeedBonus(double remHullCapNeedBonus) {
		this.remHullCapNeedBonus = remHullCapNeedBonus;
	}

	public double getRemHullCapNeedBonus() {
		return remHullCapNeedBonus;
	}
	
	public void setAfterburnerDurationBonus(double afterburnerDurationBonus) {
		this.afterburnerDurationBonus = afterburnerDurationBonus;
	}

	public double getAfterburnerDurationBonus() {
		return afterburnerDurationBonus;
	}

	// Static Methods
	static public long convertLong(Double value) {
		if (value==null)
			return 0;
		return value.longValue();
	}
	
	static public double convertDouble(Double value) {
		if (value==null)
			return 0;
		return value.doubleValue();
	}
	
	static public String removeSpace(String aString) {
		return aString.replaceAll(" ","");
	}
	
	public void addCorrection(String attName, double value) {
		ArrayList<Double> corrections = moduleCorrections.get(attName);
		if (corrections==null) {
			corrections = new ArrayList<Double>();
			moduleCorrections.put(attName,corrections);
		}
		corrections.add(value);	
	}
	
	public void addCorrectionNoStack(String attName, double value) {
		ArrayList<Double> corrections = moduleCorrectionsNoStack.get(attName);
		if (corrections==null) {
			corrections = new ArrayList<Double>();
			moduleCorrectionsNoStack.put(attName,corrections);
		}
		corrections.add(value);	
	}
	
	public void sortCorrections() {
		for (ArrayList<Double> corrections : moduleCorrections.values()) {
			if (corrections.get(0)>0)
				Collections.sort(corrections,Collections.reverseOrder());
			else
				Collections.sort(corrections);
		}
	}
	
	public String generateId() {
		StringBuffer buffer = new StringBuffer(fitProxy.getShipName());
		buffer.append(fitProxy.getName());
		buffer.append(fitProxy.getMaker());
		return DigestUtils.shaHex(buffer.toString());
	}

	public void setAftSpeedBonus(double aftSpeedBonus) {
		this.aftSpeedBonus = aftSpeedBonus;
	}

	public double getAftSpeedBonus() {
		return aftSpeedBonus;
	}

	public void setRemEnergyPowerNeedBonus(double remEnergyPowerNeedBonus) {
		this.remEnergyPowerNeedBonus = remEnergyPowerNeedBonus;
	}
	
	public void correctRemEnergyPowerNeedBonus(double b) {
		this.remEnergyPowerNeedBonus *= b;
	}

	public double getRemEnergyPowerNeedBonus() {
		return remEnergyPowerNeedBonus;
	}

	public void setMwdCapNeedBonus(double mwdCapNeedBonus) {
		this.mwdCapNeedBonus = mwdCapNeedBonus;
	}

	public double getMwdCapNeedBonus() {
		return mwdCapNeedBonus;
	}

	public void setMwdSigBonus(double mwdSigBonus) {
		this.mwdSigBonus = mwdSigBonus;
	}

	public double getMwdSigBonus() {
		return mwdSigBonus;
	}

	public void setRemArmorPowerNeedBonus(double remArmorPowerNeedBonus) {
		this.remArmorPowerNeedBonus = remArmorPowerNeedBonus;
	}

	public double getRemArmorPowerNeedBonus() {
		return remArmorPowerNeedBonus;
	}

	public void setRemShieldCpuNeedBonus(double remShieldCpuNeedBonus) {
		this.remShieldCpuNeedBonus = remShieldCpuNeedBonus;
	}

	public double getRemShieldCpuNeedBonus() {
		return remShieldCpuNeedBonus;
	}

	public void setScrambleCapNeedBonus(double scrambleCapNeedBonus) {
		this.scrambleCapNeedBonus = scrambleCapNeedBonus;
	}

	public double getScrambleCapNeedBonus() {
		return scrambleCapNeedBonus;
	}

	public void setArmorRepairCapNeedBonus(double armorRepairCapNeedBonus) {
		this.armorRepairCapNeedBonus = armorRepairCapNeedBonus;
	}

	public double getArmorRepairCapNeedBonus() {
		return armorRepairCapNeedBonus;
	}


}
